#pragma once

//	If this header is included, it must be before <windows.h>

#define NOMINMAX

#ifndef _DEBUG
#define _SECURE_SCL 0
#endif

//+------------------------------------------------------------------+
template<typename T>
inline T min(const T &a, const T &b)
{
	return a < b ? a : b;
}

//+------------------------------------------------------------------+
template<typename T>
inline T max(const T &a, const T &b)
{
	return a > b ? a : b;
}


//+------------------------------------------------------------------+
template<typename T>
inline T sqr(const T &x)
{
	return x*x;
}


//+------------------------------------------------------------------+
#include <Windows.h>
#include <intrin.h>
#include <limits>
#include <sstream>

extern "C" {
#include "dSFMT/dSFMT.h"
#include "sse_mathfun.h"
}


//+------------------------------------------------------------------+
typedef unsigned int			uint;
typedef unsigned __int64	u64;
typedef unsigned __int32	u32;
typedef unsigned __int16	u16;
typedef unsigned __int8		u8;

static const double	PI					= 3.1415926535897932384626433832795;
static const double	INV_LN2			= 1.4426950408889634073599246810019;		// 1 / ln(2)
static const double	GOLDEN_RATIO	= 1.6180339887498948482;						// "phi" = (1 + sqrt(5)) / 2;
static const double	INF_DOUBLE		= std::numeric_limits<double>::infinity();
static const double	MAX_DOUBLE		= std::numeric_limits<double>::max();
static const float	INF_FLOAT		= std::numeric_limits<float>::infinity();
static const float	MAX_FLOAT		= std::numeric_limits<float>::max();
static const double	EMPTY_VALUE		= 2147483647.0;
static const double	_0p3333			= 1.0 / 3.0;
static const float	_0p3333f			= float(_0p3333);
static const double	_0p5				= 0.5;
static const float	_0p9999f			= 1.0f - std::numeric_limits<float>::epsilon();

static const __m128d	_pd_1				= _mm_set1_pd(1.0);
static const __m128d	_pd_2				= _mm_set1_pd(2.0);
static const __m128d	_pd_3				= _mm_set1_pd(3.0);
static const __m128d	_pd_PI			= _mm_set1_pd(PI);
static const __m128	_ps_PI			= _mm_set1_ps(float(PI));
static const __m128d	_pd_0p3333		= _mm_set1_pd(_0p3333);
static const __m128d	_pd_0p5			= _mm_set1_pd(_0p5);
static const __m128	_ps_0p3333		= _mm_set1_ps(_0p3333f);
static const __m128	_ps_0p9999		= _mm_set1_ps(_0p9999f);
static const __m128d	_pd_neg1			= _mm_set1_pd(-1.0);
static const __m128	_ps_neg1			= _mm_set1_ps(-1.0f);
//     const __m128	_ps_0p5			= _mm_set1_ps(0.5f);
//     const __m128	_ps_1				= _mm_set1_ps(1.0f);
static const __m128	_ps_2				= _mm_set1_ps(2.0f);
static const __m128	_ps_3				= _mm_set1_ps(3.0f);
static const __m128	_ps_9				= _mm_set1_ps(9.0f);
static const __m128	_ps_27			= _mm_set1_ps(27.0f);
static const __m128	_ps_inf			= _mm_set1_ps(INF_FLOAT);
static const __m128i	_epi32_1			= _mm_set1_epi32(1);

//+------------------------------------------------------------------+
//	Macro for code benchmarking: just wrap the respective code with
//	BENCH_START(num_trials) and
//	BENCH_STOP
//	The time of the fastest trial is printed in processor cycles.
#define BENCH_START(TRIALS) {									\
	DWORD64	tMin = ~0,											\
				start;												\
	for(int trial = TRIALS; trial; trial--) {				\
		start = __rdtsc();

//		code goes here

#define BENCH_STOP												\
		tMin = min(tMin, __rdtsc()-start);					\
	}	std::cout << tMin << " cycles. " << std::endl;	\
}


//+------------------------------------------------------------------+
//	Logging file
extern std::ofstream logfile;
extern CRITICAL_SECTION logfileAccess;

//#ifdef DEBUG
#define LOG(msg){	EnterCriticalSection(&logfileAccess);													\
						logfile << __FILE__ << ", line " << __LINE__ << ": " << msg << std::endl;	\
						LeaveCriticalSection(&logfileAccess);													\
}
//#else
//#define LOG(msg)
//#endif

//-----------------------------------------------------------------------------
//	Shows a message box.
inline void Error(LPCWSTR text, bool warning = false)
{
	MessageBox(NULL, text, L"Error", MB_OK|(warning?MB_ICONWARNING:MB_ICONERROR));
}


//-------------------------------------------------------------------------------------------------
//				Obvious operators for SSE and SSE2 datatypes
//-------------------------------------------------------------------------------------------------

				inline	__m128		operator+	(const __m128  &x, const __m128  &y)		{	return _mm_add_ps(x,y);			}
				inline	__m128		operator-	(const __m128  &x, const __m128  &y)		{	return _mm_sub_ps(x,y);			}
				inline	__m128		operator*	(const __m128  &x, const __m128  &y)		{	return _mm_mul_ps(x,y);			}
				inline	__m128		operator/	(const __m128  &x, const __m128  &y)		{	return _mm_div_ps(x,y);			}
				inline	__m128&		operator+=	(		 __m128  &x, const __m128  &y)		{	return x = _mm_add_ps(x,y);	}
				inline	__m128&		operator-=	(		 __m128  &x, const __m128  &y)		{	return x = _mm_sub_ps(x,y);	}
				inline	__m128&		operator*=	(		 __m128  &x, const __m128  &y)		{	return x = _mm_mul_ps(x,y);	}
				inline	__m128&		operator/=	(		 __m128  &x, const __m128  &y)		{	return x = _mm_div_ps(x,y);	}

				inline	__m128d		operator+	(const __m128d &x, const __m128d &y)		{	return _mm_add_pd(x,y);			}
				inline	__m128d		operator-	(const __m128d &x, const __m128d &y)		{	return _mm_sub_pd(x,y);			}
				inline	__m128d		operator*	(const __m128d &x, const __m128d &y)		{	return _mm_mul_pd(x,y);			}
				inline	__m128d		operator/	(const __m128d &x, const __m128d &y)		{	return _mm_div_pd(x,y);			}
				inline	__m128d&		operator+=	(		 __m128d &x, const __m128d &y)		{	return x = _mm_add_pd(x,y);	}
				inline	__m128d&		operator-=	(		 __m128d &x, const __m128d &y)		{	return x = _mm_sub_pd(x,y);	}
				inline	__m128d&		operator*=	(		 __m128d &x, const __m128d &y)		{	return x = _mm_mul_pd(x,y);	}
				inline	__m128d&		operator/=	(		 __m128d &x, const __m128d &y)		{	return x = _mm_div_pd(x,y);	}

				inline	__m128d		operator-	(const __m128d	&x)								{	return _mm_sub_pd(_mm_setzero_pd(),x);	}
				inline	__m128		operator-	(const __m128	&x)								{	return _mm_sub_ps(_mm_setzero_ps(),x);	}

				inline	__m128		operator==	(const __m128  &x, const __m128  &y)		{	return _mm_cmpeq_ps(x,y);					}
				inline	__m128		operator!=	(const __m128  &x, const __m128  &y)		{	return _mm_cmpneq_ps(x,y);					}
				inline	__m128		operator>=	(const __m128  &x, const __m128  &y)		{	return _mm_cmpge_ps(x,y);					}
				inline	__m128		operator<=	(const __m128  &x, const __m128  &y)		{	return _mm_cmple_ps(x,y);					}
				inline	__m128		operator>	(const __m128  &x, const __m128  &y)		{	return _mm_cmpgt_ps(x,y);					}
				inline	__m128		operator<	(const __m128  &x, const __m128  &y)		{	return _mm_cmplt_ps(x,y);					}

				inline	__m128		operator!	(const __m128  &x)								{	return _mm_andnot_ps	(x,_mm_setzero_ps());}
				inline	__m128		operator&	(const __m128  &x, const __m128  &y)		{	return _mm_and_ps		(x,y);				}
				inline	__m128		operator|	(const __m128  &x, const __m128  &y)		{	return _mm_or_ps		(x,y);				}
				inline	__m128		operator^	(const __m128  &x, const __m128  &y)		{	return _mm_xor_ps		(x,y);				}
				inline	__m128&		operator&=	(		 __m128  &x, const __m128  &y)		{	return x = _mm_and_ps(x,y);				}
				inline	__m128&		operator|=	(		 __m128  &x, const __m128  &y)		{	return x = _mm_or_ps	(x,y);				}
				inline	__m128&		operator^=	(		 __m128  &x, const __m128  &y)		{	return x = _mm_xor_ps(x,y);				}
				inline	__m128		abs			(const __m128  &x)								{	return _mm_max_ps(x,-x);					}
template<>	inline	__m128		max<__m128>	(const __m128  &x, const __m128  &y)		{	return _mm_max_ps(x,y);						}
template<>	inline	__m128		min<__m128>	(const __m128  &x, const __m128  &y)		{	return _mm_min_ps(x,y);						}


//-------------------------------------------------------------------------------------------------
//	ternary operator: returns a where the bitmask is set else b
inline	__m128	if_ps				(__m128 mask, __m128 a, __m128 b)
{
	return _mm_and_ps(a,mask) | _mm_andnot_ps(mask,b);
}

//-------------------------------------------------------------------------------------------------
inline	__m128	pow_ps			(__m128 x, __m128 y)
{
	__m128 zero = _mm_setzero_ps();
	__m128 x_0 = _mm_cmpeq_ps(x,zero);
	__m128 y_0 = _mm_cmpeq_ps(y,zero);
	return if_ps(x_0, if_ps(y_0,zero,_ps_1), exp_ps(log_ps(x)*y));
}


//-------------------------------------------------------------------------------------------------
inline	int			round		(float   x)			{	return(x>0.0f? x+0.5f: x-0.5f);			}
inline	int			round		(double  x)			{	return(x>0.0 ? x+0.5 : x-0.5 );			}
inline	int			sign		(float	x)			{	return(x>0.0f? 1 : x<0.0f? -1 : 0);		}
inline	int			sign		(double	x)			{	return(x>0.0 ? 1 : x<0.0 ? -1 : 0);		}
inline	bool			isPow2	(unsigned n)		{	return (n&(n-1)) == 0;						}
inline	int			nextEven	(int n)				{	return (n+1) & ~1;							}
inline	unsigned		nextEven	(unsigned n)		{	return (n+1) & ~1;							}
inline	int			nextPow2	(int k)
{
	k--;
	for(int i=1; i<sizeof(int)*CHAR_BIT; i<<=1)
		k = k | k >> i;
	return k+1;
}



 //-----------------------------------------------------------------------------
template<typename T>
inline T convert(std::wstring const &s)
{
	std::wistringstream i(s);
	T x;
	i >> x;
	return x;
}

 //-----------------------------------------------------------------------------
template<typename T>
inline T convert(std::string const &s)
{
	std::istringstream i(s);
	T x;
	i >> x;
	return x;
}

 //-----------------------------------------------------------------------------
template<typename T>
inline std::string stringify(const T &x)
{
	std::ostringstream o;
	o << x;
	return o.str();
}

 //-----------------------------------------------------------------------------
template<typename T>
inline std::wstring wstringify(const T &x)
{
	std::wostringstream o;
	o << x;
	return o.str();
}


//-------------------------------------------------------------------------------------------------
//	convert float array to double array
inline	void			cvtps2pd		(const float *src, double *dst, uint n)
{
	__m128d	*d =(__m128d*)dst;
	__m128	*s = (__m128*)src,
				*lim = s + (n>>2);

	while(s < lim)
	{
		d[0] = _mm_cvtps_pd(s[0]);
		d[1] = _mm_cvtps_pd(_mm_movehl_ps(_mm_setzero_ps(),s[0]));
		s++;
		d+=2;
	}
}

//-------------------------------------------------------------------------------------------------
//	convert double array to float array
inline	void			cvtpd2ps		(const double *src, float *dst, uint n)
{
	__m128	*d = (__m128*)dst;
	__m128d	*s =(__m128d*)src,
				*lim = s + (n>>1);

	while(s < lim)
	{
		*d++ = _mm_movelh_ps(_mm_cvtpd_ps(s[0]),_mm_cvtpd_ps(s[1]));
		s+=2;
	}
}




//-------------------------------------------------------------------------------------------------
union _CRT_ALIGN(16) M128
{
	__m128              m128;
	__m128d             m128d;
	__m128i             m128i;
	float               f32[4];
	double              f64[2];
	__int8              i8[16];
	__int16             i16[8];
	__int32             i32[4];
	__int64             i64[2];
	unsigned __int8     u8[16];
	unsigned __int16    u16[8];
	unsigned __int32    u32[4];
	unsigned __int64    u64[2];

//----
	operator const	__m128&() const	{	return m128;	}
	operator			__m128&()			{	return m128;	}
};


